From 1a7e39480245e75587e85b02c083f83275cc9929 Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Thu, 16 Apr 2026 16:26:47 +0000 Subject: [PATCH] [PATCH 2/3] lib-storage: thread - Limit References: header msgid count to prevent O(N^2) CPU usage Cap per-message References ingestion at MAIL_THREAD_REFERENCES_MAX (1000) in mail_thread_map_add_mail(). Without this limit a single crafted email with N unique Message-IDs triggers N(N-1)/2 ancestor traversals in thread_node_has_ancestor(), allowing unauthenticated DoS via mail delivery. Gbp-Pq: Name 0002-lib-storage-thread-Limit-References-header-msgid-cou.patch --- src/lib-storage/index/index-thread-private.h | 4 ++++ src/lib-storage/index/index-thread.c | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib-storage/index/index-thread-private.h b/src/lib-storage/index/index-thread-private.h index ed2837b..563c3a3 100644 --- a/src/lib-storage/index/index-thread-private.h +++ b/src/lib-storage/index/index-thread-private.h @@ -22,6 +22,10 @@ #define MAIL_THREAD_NODE_REF_MSGID 0 #define MAIL_THREAD_NODE_REF_INREPLYTO 1 #define MAIL_THREAD_NODE_REF_REFERENCES1 2 +/* Maximum number of References: header message-IDs stored per message. + Prevents O(N²) CPU usage in thread_node_has_ancestor() when a single + crafted message contains an abnormally long reference chain. */ +#define MAIL_THREAD_REFERENCES_MAX 1000 struct mail_thread_node { /* UID of the message, or 0 for dummy nodes */ diff --git a/src/lib-storage/index/index-thread.c b/src/lib-storage/index/index-thread.c index e20a970..a58f968 100644 --- a/src/lib-storage/index/index-thread.c +++ b/src/lib-storage/index/index-thread.c @@ -280,7 +280,8 @@ mail_thread_map_add_mail(struct mail_thread_context *ctx, struct mail *mail) ref_index, msgid); } } T_END; - } while (msgid != NULL); + } while (msgid != NULL && + ref_index < MAIL_THREAD_NODE_REF_REFERENCES1 + MAIL_THREAD_REFERENCES_MAX); } else { /* no References:, use In-Reply-To: */ if (thread_get_mail_header(mail, HDR_IN_REPLY_TO, -- 2.39.5